
//  CBluetoothManager.cpp
//  testBluetooth2
//
//  Created by idiot.lin on 15/1/14.
//  Copyright (c) 2015年 ZKTeco Inc. All rights reserved.
//
#include <pthread.h>
#include <unistd.h>
#include <CoreBluetooth/CoreBluetooth.h>
#import "platform.h"
#include "CBluetoothManager.h"
#include "NSString+hexStringToData.h"
#include "CBPeripheral+Addition.h"
#import "CBUUID+addition.h"
#import "zkDevice.h"
#import "DeviceManager.h"
#define BT_ReadTimeOut 5
#define BT_CoonnectTimeOut 5

//
BOOL isWriteSuccess = NO;
BOOL isReadSuccess = NO;
BOOL isMacSuccess = NO;


opeRet opeState = ConnectUnkown;
int blueToothState = CBCentralManagerStateUnknown;

dispatch_semaphore_t semBtInit = dispatch_semaphore_create(0);
dispatch_semaphore_t semBtRead = dispatch_semaphore_create(0);
dispatch_semaphore_t semBtWrite = dispatch_semaphore_create(0);
dispatch_semaphore_t semBtMac  = dispatch_semaphore_create(0);
dispatch_queue_t sysQueue = dispatch_queue_create("serial.dispatch.queue", DISPATCH_QUEUE_SERIAL);
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

CBCentralManager *tCbManager = nil;
discoverPeripheral findPeripheral = nil;
handleDiscoverPeripheral foundPeripheral = nil;
handleDidDisconnectPeripheral disconnectPeripheral = nil;

void ExLog(NSString *strFmt)
{
    UDK_DUMP("", [strFmt UTF8String], strFmt.length);
}

@interface ZKBluetoothConnectManager:NSObject<CBCentralManagerDelegate,CBPeripheralDelegate>

@property(nonatomic,strong)CBCharacteristic *cbCharacter;//数据传输文件用的特征
@property (nonatomic, strong) CBCharacteristic *cbCharacterMac;//用于获取mac地址的特征
@property(nonatomic,strong)NSMutableData *btData;   //-------
@property(nonatomic,strong)NSMutableData *btMacAddrData;   //-------

@property(nonatomic,weak)NSMutableArray *btList;
@property(nonatomic,assign)CBluetoothManager *blueMananger;
@property(nonatomic,assign)bool mBtPrepare;
@property(nonatomic,strong)NSMutableDictionary *dicDeviceMap;

@end

@implementation ZKBluetoothConnectManager

- (instancetype)init
{
    if (self = [super init])
    {
        _mBtPrepare = false;
        self.btData = [[NSMutableData alloc] init];
        self.dicDeviceMap = [[NSMutableDictionary alloc] init];
        sysQueue = dispatch_queue_create("com.sysBlueTooth.readWrite", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)clearBuffer
{
    [lock lock];
        self.btData = [[NSMutableData alloc] init];
    [lock unlock];
}

//扫描设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    //<---发现设备

    NSArray *aryList = [advertisementData valueForKey:@"kCBAdvDataServiceUUIDs"];
    peripheral.btServiceUUIDList = aryList;
    DeviceManager *manager = [[DeviceManager alloc]init];

    DeviceType deviceType = [manager getDeviceType:advertisementData];
    if (deviceType == UnKnownDevice) {
        peripheral.deviceType = BiolockDivice;
    }
    else
    {
        peripheral.deviceType = deviceType;
    }
   
    if(_blueMananger->scanByUUid() == false)
    {
        BOOL retFind = [self isValidServiceUUId:peripheral];
        if (!retFind)
        {
            return ;
        }
    }

    ExLog([NSString stringWithFormat:@"find device ==== [%@],localName = [%@],rssi = [%@]",peripheral,peripheral.btLocalName,RSSI]);
    BOOL isValidRssi = YES;//RSSI.doubleValue > - 87;
    if (![self.btList containsObject:peripheral] && isValidRssi)
    {
        [self.btList addObject:peripheral];
        NSString *strPrime = [NSString stringWithFormat:@"%p",peripheral];
        [self.dicDeviceMap setObject:peripheral forKey:strPrime];
        NSArray *keyArr = [advertisementData allKeys];
        if ([keyArr containsObject:@"kCBAdvDataLocalName"])
        {
            if([peripheral respondsToSelector:@selector(setBtLocalName:)])
            {
                peripheral.btLocalName = [advertisementData objectForKey:@"kCBAdvDataLocalName"];
            }
        }

        if (foundPeripheral)
        {
           foundPeripheral(central ,peripheral,advertisementData,RSSI);
        }
    }
}

- (BOOL)isValidServiceUUId:(CBPeripheral *)peripheral
{
    BOOL retFind = NO;
    if (peripheral.btServiceUUIDList.count > 1 || peripheral.btServiceUUIDList.count == 0 ) {
        return retFind;
    }

    vector<PeriUuidInfo>uidList = _blueMananger->perUuidList;
    for (int i = 0;i < uidList.size();i++)
    {
        for (CBUUID *uid in peripheral.btServiceUUIDList)
        {
            string uuid = uidList[i].serviceUUID;
            NSString *strUuid = [NSString stringWithUTF8String:uuid.c_str()];
            if ([strUuid isEqualToString:uid.UUIDString])
            {
                retFind = YES;
                break;
            }
        }
    }
    return retFind;
}

- (CBService *)getValidPheralUuid:(CBPeripheral *)peripheral
{
    CBService *info = nil;
    for (CBService *service in peripheral.services)
    {

        ExLog([[NSString alloc] initWithData:service.UUID.data encoding:NSUTF8StringEncoding]);
        vector<PeriUuidInfo>uidList = _blueMananger->perUuidList;
        for (int i = 0;i < uidList.size();i++) {
            string uuid = uidList[i].serviceUUID;
            NSString *strUuid = [NSString stringWithUTF8String:uuid.c_str()];
            if ([strUuid isEqualToString:service.UUID.zkUUIDString]) {
                info = service;
                break;
            }
        }
    }
    return info;
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    //设置代理
    peripheral.delegate = self;
    [peripheral discoverServices:peripheral.btServiceUUIDList];
    ExLog([NSString stringWithFormat:@"didConnectPeripheral ----peripheral [%@]",peripheral]);
}

//连接失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    opeState = ConnectFailed;
    ExLog([NSString stringWithFormat:@"didFailToConnectPeripheral ----peripheral = [%@] ",peripheral]);
    ExLog([NSString stringWithFormat:@"blueTooth state = [%ld]",central.state]);
}

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    ExLog([NSString stringWithFormat:@"didDisconnectPeripheral ----peripheral = [%@] ",peripheral]);
    opeState = DisConnect;
    ExLog([NSString stringWithFormat:@"blueTooth state = [%ld]",central.state]);
    [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationBlueToothStateChange object:nil userInfo:@{DICKeyBtOpeState:@(DisConnect)}];
    if (disconnectPeripheral)
    {
        disconnectPeripheral(peripheral,error);
    }
}
//手机蓝牙状态改变时会调用此方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    ExLog(@"centralManagerDidUpdateState ---");
    ExLog([NSString stringWithFormat:@"blueTooth state = [%ld]",central.state]);

    self.mBtPrepare = true;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,
                  ^{
                      dispatch_semaphore_signal(semBtInit);
    });
    
    if (central.state == CBCentralManagerStatePoweredOff)//关闭蓝牙
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"openBLE" object:@"openBLE"];
    }

    blueToothState = central.state;
    [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationBlueToothLiveState object:nil userInfo:@{DICKeyBlueToothState:@(central.state)}];

}


- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    //找寻服务中指定的特征
    ExLog(@"didDiscoverServices--------------");

    if (!error)
    {
        CBService * service = [self getValidPheralUuid:peripheral];
        if (service)
        {
            [peripheral discoverCharacteristics:nil forService:service];
        }
    }
    else
    {
        opeState = FindServiceFailed;
    }
}

- (PeriUuidInfo )getPeriUuidInfo:(CBService *)service
{
    PeriUuidInfo uuidInfo ;
    for (int i = 0;i< _blueMananger->perUuidInfo().size();i++) {
        PeriUuidInfo info = _blueMananger->perUuidInfo()[i];
        NSString *strServiceUuid = [NSString stringWithUTF8String:info.serviceUUID.c_str()];
        if ([strServiceUuid isEqualToString:service.UUID.zkUUIDString]) {
            uuidInfo = info;
            break;
        }
    }
    return uuidInfo;
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    // 以防万一，我们再次遍历数组
    ExLog(@"didDiscoverCharacteristicsForService--------------");
    @try
    {
        if (!error)
        {
            PeriUuidInfo uuidInfo = [self getPeriUuidInfo:service];
            NSString * strWriteCharacter0 = [NSString stringWithUTF8String:uuidInfo.strCharactersticsWriteUUID0.c_str()];
            NSString * strCmdCharacter = [NSString stringWithUTF8String:uuidInfo.strChracterCmdUUID.c_str()];
            for (CBCharacteristic *characteristic in service.characteristics)
            {
                // 确保该特征是我们需要的那个
                const CBCharacteristicProperties properties = [characteristic properties];
                if (CBCharacteristicPropertyNotify & properties)
                {
                    [peripheral setNotifyValue:YES forCharacteristic:characteristic];
                }

                if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:strWriteCharacter0]])
                {
                    self.cbCharacter = characteristic;
                    // 如果是，订阅它。一旦特征的值被更新，用-setNotifyValue:forCharacteristic:，周边被请求通知它的代理。
                    //监听特征
                    ExLog([NSString stringWithFormat:@"didDiscoverCharacteristicsForService write character = [%@]",characteristic]);
                }

                if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:strCmdCharacter]])
                {
                    self.cbCharacterMac = characteristic;
                    [peripheral setNotifyValue:YES forCharacteristic:self.cbCharacterMac];
                    ExLog([NSString stringWithFormat:@"didDiscoverCharacteristicsForService mac addr character = [%@]",characteristic]);
                }
            }
            if (self.cbCharacter != nil && self.cbCharacterMac != nil)
            {
                opeState = ConnectSucess;
            }
        }
        else
        {
            opeState = FindServiceFailed;
        }
    } @catch (NSException *exception)
    {
        opeState = FindServiceFailed;
        ExLog([NSString stringWithFormat:@"failed to discover characteristic "]);

    } @finally
    {

    }
}


- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    ExLog([NSString stringWithFormat:@"didUpdateValueForCharacteristic = [%@]",characteristic.value]);

    if (characteristic == self.cbCharacterMac)  //这个是命令通道
    {
        self.btMacAddrData = (NSMutableData *)characteristic.value;
        isMacSuccess = error == nil;
        dispatch_semaphore_signal(semBtMac);
    }
    else
    {
        [lock lock];
        NSData *newData = characteristic.value;
        [self.btData appendData:newData];
        [lock unlock];
        isReadSuccess = error == nil;
    }
}



//数据写入调用该方法，前提是写入时的type == CBCharacteristicWriteWithResponse，否则不会调用该方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error == nil)
    {
        isWriteSuccess = YES;
    }else
    {
        isWriteSuccess = NO;
    }

    dispatch_semaphore_signal(semBtWrite);
}

@end


ZKBluetoothConnectManager *bTmanager = [[ZKBluetoothConnectManager alloc] init];

CBluetoothManager::CBluetoothManager()
{
    connectManager = (__bridge void *)bTmanager;
    tCbManager = [[CBCentralManager alloc] initWithDelegate:bTmanager queue:dispatch_get_global_queue(0, 0)
                  ];
    cbCenterManager = tCbManager;
    bTmanager.blueMananger = this;
}
CBluetoothManager::~CBluetoothManager()
{
    CBCentralManager *tCbManager = cbCenterManager;
    tCbManager.delegate = nil;
}

void CBluetoothManager::asyncSearchBlueDevice(NSMutableArray *aryDevice,handleDiscoverPeripheral findPeripheral,competion compet,int timeOut)
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^()
                   {
                       bool ret =  waitBtPrepared();
                       int intRet = 1;
                       if (ret == true)
                       {
                           NSMutableArray *aryPerUuidInfo  = nil;
                           if ( true == _scanByUUid)
                           {
                               aryPerUuidInfo = [NSMutableArray new];
                               for (int i = 0;i< perUuidList.size();i++) {
                                   NSString * serviceUuid = [NSString stringWithUTF8String:perUuidList[i].serviceUUID.c_str()];
                                   CBUUID *uuid = [CBUUID UUIDWithString:serviceUuid];
                                   [aryPerUuidInfo addObject:uuid];
                               }
                           }

                           foundPeripheral = [findPeripheral copy];
                           CBCentralManager *tCbManager = cbCenterManager;
                           NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
                           [tCbManager scanForPeripheralsWithServices:aryPerUuidInfo options:options];



                           ZKBluetoothConnectManager *btMananger = (__bridge ZKBluetoothConnectManager *)connectManager;
                           btMananger.btList = aryDevice;
                           int64_t delayInSeconds = timeOut;
                           dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
                           dispatch_after(popTime, dispatch_get_global_queue(0, 0), ^(void)
                                          {
                                              compet(nil);
                                              [tCbManager stopScan];
                                          });
                       }
                       else
                       {
                           intRet = 0;
                       }

                   });
}

int CBluetoothManager::searchBlueDevice(NSMutableArray *aryDevice,int timeOut)
{
    bool ret =  waitBtPrepared();
    int intRet = 1;
    if (ret == true)
    {
        NSMutableArray *aryPerUuidInfo  = nil;
        if ( true == _scanByUUid)
        {
            aryPerUuidInfo = [NSMutableArray new];
            for (int i = 0;i< perUuidList.size();i++) {
                NSString * serviceUuid = [NSString stringWithUTF8String:perUuidList[i].serviceUUID.c_str()];
                CBUUID *uuid = [CBUUID UUIDWithString:serviceUuid];
                [aryPerUuidInfo addObject:uuid];
            }
        }

        CBCentralManager *tCbManager = cbCenterManager;
        NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
        [tCbManager scanForPeripheralsWithServices:aryPerUuidInfo options:options];
        
        ZKBluetoothConnectManager *btMananger = (__bridge ZKBluetoothConnectManager *)connectManager;
        btMananger.btList = aryDevice;
        int64_t delayInSeconds = timeOut;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_global_queue(0, 0), ^(void)
                       {
                           dispatch_semaphore_signal(semBtInit);
                       });
        dispatch_semaphore_wait(semBtInit,  dispatch_time(DISPATCH_TIME_NOW, (timeOut +2)*NSEC_PER_SEC));
    }
    else
    {
        intRet = 0;
    }

    return intRet;
}

void CBluetoothManager::setHandleDisconnect(handleDidDisconnectPeripheral disconnectHandle)
{
    disconnectPeripheral = disconnectHandle;
};

bool CBluetoothManager::connect(CBPeripheral *peripheral)
{
    opeState = ConnectUnkown;
    if (peripheral == nil)
    {
        return false;
    }
    
    ZKBluetoothConnectManager *blueManager = (__bridge ZKBluetoothConnectManager *)connectManager;
    
    CBPeripheral * bpr = peripheral;
    CBCentralManager *manager = cbCenterManager;
    bprContrl = bpr;
    [manager connectPeripheral:bpr options:nil];
    
    clock_t start, end;
    double cpu_time_used;
    
    start = clock();
    while (1)
    {
        end = clock();
        cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
        if (cpu_time_used > 5*10)
        {
            break;
        }
        if (opeState == ConnectSucess || opeState == ConnectFailed || DisConnect == opeState)
        {
            break;
        }
    }
    
    ExLog([NSString stringWithFormat:@"connect ret ==== [%d]\n",opeState]);
    return opeState == ConnectSucess;
}

bool CBluetoothManager::connect(char *cbPerKey,unsigned int port,int speed)
{
    opeState = ConnectUnkown;
    if (cbPerKey == nil)
    {
        return false;
    }
    
    ZKBluetoothConnectManager *blueManager = (__bridge ZKBluetoothConnectManager *)connectManager;
    
    CBPeripheral * bpr = [blueManager.dicDeviceMap valueForKey:[NSString stringWithFormat:@"%s",cbPerKey]];
    if (bpr == nil)
    {
        return false;
    }
    
    
    CBCentralManager *manager = cbCenterManager;
    bprContrl = bpr;
    [manager connectPeripheral:bpr options:nil];
    
    clock_t start, end;
    double cpu_time_used;
    
    start = clock();
    while (1)
    {
        end = clock();
        cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
        if (cpu_time_used > 5*10)
        {
            break;
        }
        if (opeState == ConnectSucess || opeState == ConnectFailed || DisConnect == opeState)
        {
            break;
        }
    }
    
    ExLog([NSString stringWithFormat:@"connect ret ==== [%d]\n",opeState]);
    return opeState == ConnectSucess;
}


int CBluetoothManager::read(char *buf, int offset, int count)
{
    [lock lock];
    if (opeState != ConnectSucess) {
        [lock unlock];
        return -2;
    }
   __block int retCount = 0;

    if (isReadSuccess == YES)
    {
        ZKBluetoothConnectManager *btMananger = (__bridge ZKBluetoothConnectManager *)connectManager;

            if (btMananger.btData != nil)
            {
                retCount = (int)btMananger.btData.length;
                unsigned char *data = (unsigned char *)btMananger.btData.bytes;
                if (retCount < count)
                {
                    count = retCount;
                }
                else
                {
                    retCount = count;
                }

                memcpy(buf+offset, data,count);
                NSRange index = {static_cast<NSUInteger>(offset),static_cast<NSUInteger>(count)};
                [btMananger.btData replaceBytesInRange:index withBytes:NULL length:0];
            }
    }

    [lock unlock];
    return retCount;
}


int CBluetoothManager::write(char *buf, int offset, int count)
{
    if (opeState != ConnectSucess) {
        return -2;
    }
    CBPeripheral *bpr = bprContrl;

    NSData *data = [NSData dataWithBytes:buf length:count];
    ZKBluetoothConnectManager *btMananger = (__bridge ZKBluetoothConnectManager *)connectManager;

    //发送正确代码
    if (btMananger.cbCharacter == nil) {
        return -1;
    }
    [bpr writeValue:data forCharacteristic:btMananger.cbCharacter type:CBCharacteristicWriteWithResponse];
    int wtCount = -1;
    dispatch_semaphore_wait(semBtWrite, dispatch_time(DISPATCH_TIME_NOW, 4*NSEC_PER_SEC));
    
    if (isWriteSuccess == YES)
    {
        wtCount = btMananger.cbCharacter.value.length;
        wtCount = count;

        ExLog([NSString stringWithFormat:@"App wtCount ====== [%d],value = [%@]",wtCount,data]);
    }

    return wtCount;
}

int CBluetoothManager::writeCmd(char *buf, int offset, int count)
{
    CBPeripheral *bpr = bprContrl;
    NSData *data = [NSData dataWithBytes:buf length:count];
    ZKBluetoothConnectManager *btMananger = (__bridge ZKBluetoothConnectManager *)connectManager;
    if (btMananger.cbCharacterMac == nil) {
        return -1;
    }
    [bpr writeValue:data forCharacteristic:btMananger.cbCharacterMac type:CBCharacteristicWriteWithResponse];

    int wtCount = -1;
    dispatch_semaphore_wait(semBtMac, dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC));
    if (isWriteSuccess == YES)
    {
        wtCount = data.length;
    }

    return wtCount;
}

int CBluetoothManager::readCmd(char *buf,int offset,int count)
{
    int retCount = -1;

    if (isMacSuccess == YES)
    {
        ZKBluetoothConnectManager *btMananger = (__bridge ZKBluetoothConnectManager *)connectManager;
        /*---------------*/
        if (btMananger.btMacAddrData != nil)
        {
            retCount = (int)btMananger.btMacAddrData.length;
            unsigned char *data = (unsigned char *)btMananger.btMacAddrData.bytes;
            memccpy(buf+offset, data, 'B',retCount);
            btMananger.btMacAddrData = nil;
        }
    }
    return retCount;
}

bool CBluetoothManager::waitBtPrepared()
{
    dispatch_semaphore_wait(semBtInit, dispatch_time(DISPATCH_TIME_NOW, 4*NSEC_PER_SEC));
    ZKBluetoothConnectManager *connMananger =  (__bridge  ZKBluetoothConnectManager *)connectManager;
    bool ret = connMananger.mBtPrepare;
    ExLog(@"");
    return ret;
}

bool CBluetoothManager::disconnect()
{
    if (bprContrl == nil)
    {
        return false;
    }
    CBPeripheral *cbPer = bprContrl;
    CBCentralManager *cbCen = cbCenterManager;
    [cbCen cancelPeripheralConnection:cbPer];
    opeState = DisConnect;
    if ( bTmanager.cbCharacter.isNotifying)
    {
        // It is notifying, so unsubscribe
        [cbPer setNotifyValue:NO forCharacteristic:bTmanager.cbCharacter];
    }
    if (bTmanager.cbCharacterMac.isNotifying) {
        [cbPer setNotifyValue:NO forCharacteristic:bTmanager.cbCharacterMac];
    }

    return true;
}

CBluetoothManager *CBluetoothManager::instance()
{
    static CBluetoothManager *instance = nil;
    
    if (nil == instance)
    {
        instance = new CBluetoothManager();
    }
    return instance;
}

void CBluetoothManager::clearBuffer()
{
    [bTmanager clearBuffer];
}

void CBluetoothManager::logBlueTooth()
{

}

void CBluetoothManager::configureBlueTooth(std::vector<PeriUuidInfo>perList,bool scanType)
{
    _scanByUUid = scanType;
    perUuidList = perList;
}

bool CBluetoothManager::scanByUUid()
{
    return _scanByUUid;
}

vector<PeriUuidInfo> CBluetoothManager::perUuidInfo()
{
    return perUuidList;
}

CBCentralManager *CBluetoothManager::getCenterManager()
{
    return cbCenterManager;
}

int CBluetoothManager::btDeviceState()
{
    return (CBCentralManagerState)blueToothState;
}






